#include "videodecode.h"
#include <qdatetime.h>
#include <QDebug>
#include <QMutex>

extern "C"   // 用C规则编译指定的代码
{
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
}

#define ERROR_LEN 1024
#define PRINT_LOG 1 // 假设启用了日志

VideoDecode::VideoDecode()
{
    initFFmpeg();

    m_error = new char[ERROR_LEN];

#if defined(Q_OS_WIN)
    m_inputFormat = av_find_input_format("dshow");
#elif defined(Q_OS_LINUX)
    m_inputFormat = av_find_input_format("video4linux2");
#elif defined(Q_OS_MAC)
    m_inputFormat = av_find_input_format("avfoundation");
#endif

    if (!m_inputFormat)
    {
        qWarning() << "查询AVInputFormat失败！";
    }
}

VideoDecode::~VideoDecode()
{
    close();
    delete[] m_error; // 释放错误字符串内存
}

void VideoDecode::initFFmpeg()
{
    static bool isFirst = true;
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    if (isFirst)
    {
        avformat_network_init();
        avdevice_register_all();
        isFirst = false;
    }
}

bool VideoDecode::open(const QString& url)
{
    if (url.isNull())
        return false;

    AVDictionary* dict = nullptr;
    // 尝试强制 MJPEG，因为很多摄像头默认输出这个
    // 如果你的摄像头默认输出 RAW，可以注释掉下面这行
    av_dict_set(&dict, "input_format", "mjpeg", 0);
    // 设置分辨率和帧率（可选，如果摄像头支持）
    // av_dict_set(&dict, "video_size", "1280x720", 0);
    // av_dict_set(&dict, "framerate", "30", 0);

    int ret = avformat_open_input(&m_formatContext,
                                  url.toStdString().data(),
                                  m_inputFormat,
                                  &dict); // 注意这里传 dict 的地址

    if (dict) // 不论成功失败，都要释放字典
    {
        av_dict_free(&dict);
    }

    if (ret < 0)
    {
        showError(ret);
        free();
        return false;
    }

    ret = avformat_find_stream_info(m_formatContext, nullptr);
    if (ret < 0)
    {
        showError(ret);
        free();
        return false;
    }

    // --- 获取视频流和解码器 ---
    AVCodec* codec = nullptr; // 修改：需要非 const 指针以便后续打开
    m_videoIndex = av_find_best_stream(m_formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, const_cast<const AVCodec**>((const AVCodec**)&codec), 0); // 传递 codec 地址
    if (m_videoIndex < 0)
    {
        showError(m_videoIndex);
        free();
        return false;
    }

    AVStream* videoStream = m_formatContext->streams[m_videoIndex];

    if (!codec) { // 如果 av_find_best_stream 没有找到合适的解码器
        qWarning() << "未能找到视频流的解码器!";
        free();
        return false;
    }

    m_size.setWidth(videoStream->codecpar->width);
    m_size.setHeight(videoStream->codecpar->height);
    m_frameRate = rationalToDouble(&videoStream->avg_frame_rate);
    m_totalFrames = videoStream->nb_frames; // 可能不准确

    qInfo() << QString("视频流信息: 分辨率:[w:%1,h:%2] 格式:%3(%4) 帧率:%5 解码器:%6")
                   .arg(m_size.width()).arg(m_size.height())
                   .arg(av_get_pix_fmt_name((AVPixelFormat)videoStream->codecpar->format)) // 获取像素格式名称
                   .arg(videoStream->codecpar->format)
                   .arg(m_frameRate)
                   .arg(codec->name); // 使用找到的解码器名称

    // --- 初始化解码器上下文 ---
    m_codecContext = avcodec_alloc_context3(codec); // 使用找到的解码器
    if (!m_codecContext)
    {
        qWarning() << "创建视频解码器上下文失败！";
        free();
        return false;
    }

    // 从视频流参数填充解码器上下文
    ret = avcodec_parameters_to_context(m_codecContext, videoStream->codecpar);
    if (ret < 0)
    {
        showError(ret);
        free();
        return false;
    }

    m_codecContext->flags2 |= AV_CODEC_FLAG2_FAST; // 允许加速技巧
    // m_codecContext->thread_count = 4; // 可以设置解码线程数

    // --- 打开解码器 ---
    ret = avcodec_open2(m_codecContext, codec, nullptr); // 打开找到的解码器
    if (ret < 0)
    {
        showError(ret);
        free();
        return false;
    }

    // 分配 Packet 和 Frame
    m_packet = av_packet_alloc();
    if (!m_packet) { free(); return false; }

    // m_frame 用于存储最终转换后的 YUV420P 帧
    m_frame = av_frame_alloc();
    if (!m_frame) { free(); return false; }
    m_frame->format = AV_PIX_FMT_YUV420P; // 明确目标格式
    m_frame->width = m_size.width();
    m_frame->height = m_size.height();
    // 为 m_frame 分配内存，以便 sws_scale 可以写入
    ret = av_frame_get_buffer(m_frame, 0); // 使用默认对齐方式
    if (ret < 0) {
        qWarning() << "无法为目标 YUV420P 帧分配内存";
        showError(ret);
        free();
        return false;
    }

    // 分配一个临时 Frame 用于存储解码器的输出 (解码后的原始像素格式)
    m_decodedFrame = av_frame_alloc();
    if (!m_decodedFrame) { free(); return false; }


    // --- 初始化 SwsContext ---
    // 注意：源格式现在是解码器输出的格式 (m_codecContext->pix_fmt)
    m_swsContext = sws_getCachedContext(m_swsContext,
                                        m_codecContext->width,      // 输入宽度 (来自解码器)
                                        m_codecContext->height,     // 输入高度 (来自解码器)
                                        m_codecContext->pix_fmt,    // 输入像素格式 (来自解码器)
                                        m_size.width(),             // 输出宽度 (目标宽度)
                                        m_size.height(),            // 输出高度 (目标高度)
                                        AV_PIX_FMT_YUV420P,         // 输出像素格式 (目标格式)
                                        SWS_BILINEAR,               // 缩放算法
                                        nullptr, nullptr, nullptr);
    if (!m_swsContext)
    {
        qWarning() << "sws_getCachedContext() Error！";
        free();
        return false;
    }

    qInfo() << "VideoDecode 打开成功.";
    return true;
}


AVFrame* VideoDecode::read()
{
    if (!m_formatContext || !m_codecContext) {
        return nullptr;
    }

    int ret = 0;
    while (true) {
        // 1. 从解码器接收帧
        ret = avcodec_receive_frame(m_codecContext, m_decodedFrame);
        if (ret == 0) {
            // 成功解码一帧
            if (!m_decodedFrame->data[0]) { // 检查解码帧本身
                qWarning() << "avcodec_receive_frame succeeded but decodedFrame->data[0] is NULL!";
                av_frame_unref(m_decodedFrame);
                continue;
            }

            // 2. 进行颜色空间转换到成员变量 m_frame (YUV420P)
            if (convertToYUV420P(m_decodedFrame)) { // 结果写入 m_frame

                // --- ☆☆☆ 修改核心 ☆☆☆ ---
                // 克隆 m_frame，创建一个新的 AVFrame 实例，包含数据的副本
                AVFrame* frame_to_return = av_frame_clone(m_frame);
                if (!frame_to_return) {
                    qWarning() << "Failed to clone the converted frame!";
                    // 即使克隆失败，也要清理本次解码的帧
                    av_frame_unref(m_decodedFrame);
                    continue; // 尝试下一个
                }

                // 清理本次使用的解码帧 m_decodedFrame
                av_frame_unref(m_decodedFrame);

                // 返回克隆后的新帧，调用者 (VideoMeetingUI) 负责释放它
                return frame_to_return;
                // --- ☆☆☆ 修改结束 ☆☆☆ ---

            } else {
                // 转换失败 (日志已在 convert 函数内部打印)
                av_frame_unref(m_decodedFrame); // 清理解码后的帧
                // 继续尝试读取下一个包
            }
        } else if (ret == AVERROR(EAGAIN)) {
            // 需要更多的数据包
        } else if (ret == AVERROR_EOF) {
            // 解码器已刷新
            qInfo() << "avcodec_receive_frame: EOF";
            // 尝试发送 flush 包后再次接收，如果仍然是 EOF，则真正结束
            avcodec_send_packet(m_codecContext, nullptr); // 发送空包尝试刷新
            // 继续循环顶部尝试接收
            continue; // 或者直接 return nullptr; 取决于是否要确保完全 flush
            // return nullptr; // 文件或流结束
        } else {
            // 解码错误
            showError(ret);
            return nullptr;
        }

        // 3. 如果需要更多数据 (EAGAIN)，读取下一个包
        av_packet_unref(m_packet);
        ret = av_read_frame(m_formatContext, m_packet);
        if (ret < 0) {
            if (ret != AVERROR_EOF) {
                showError(ret);
            } else {
                qInfo() << "av_read_frame: EOF";
                // 文件结束，尝试刷新解码器
                avcodec_send_packet(m_codecContext, nullptr);
                // 继续循环顶部尝试接收剩余帧
                continue;
            }
            return nullptr;
        }

        // 4. 只处理视频包
        if (m_packet->stream_index != m_videoIndex) {
            av_packet_unref(m_packet);
            continue;
        }

        // 5. 将包发送给解码器
        ret = avcodec_send_packet(m_codecContext, m_packet);
        // 处理发送错误 (EAGAIN, EOF, etc.)
        if (ret < 0) {
            showError(ret);
            av_packet_unref(m_packet); // 清理发送失败的包
            if (ret == AVERROR(EAGAIN)) {
                // 理论上不应在这里立即 EAGAIN，因为上面先 receive 了
                qWarning() << "avcodec_send_packet returned EAGAIN unexpectedly";
                // 应该继续循环顶部先 receive
            } else if (ret == AVERROR_EOF) {
                qInfo() << "avcodec_send_packet: EOF signal sent, continuing to receive remaining frames";
                // 继续循环以接收剩余帧
            } else {
                return nullptr; // 其他发送错误
            }
        }
        // 包发送成功（或错误已处理），继续循环顶部尝试接收帧
        // 注意：不需要在这里 unref packet，因为 send_packet 成功后，
        // packet 的所有权（引用计数）可能被解码器持有，
        // 在下一次循环开始时或接收帧后 unref 即可。
        // 在循环开始时 av_packet_unref(m_packet) 是安全的。

    } // end while(true)
}



// 修改 toYUV420P 函数，现在输入是解码后的帧
bool VideoDecode::convertToYUV420P(const AVFrame* decodedFrame)
{
    if (!m_swsContext || !decodedFrame || !m_frame || !m_frame->data[0]) { // 检查 m_frame 的 buffer 是否分配
        qWarning() << "convertToYUV420P: Invalid context or frame pointers.";
        return false;
    }

    //// --- 添加调试输出 ---
    //qDebug() << "convertToYUV420P: Input frame format:" << av_get_pix_fmt_name((AVPixelFormat)decodedFrame->format)
    //         << "(" << decodedFrame->format << ")"
    //         << "size:" << decodedFrame->width << "x" << decodedFrame->height
    //         << "linesizes:" << decodedFrame->linesize[0] << decodedFrame->linesize[1] << decodedFrame->linesize[2]; // << decodedFrame->linesize[3]; // 通常 planar YUV 只有 3 个 plane
    //qDebug() << "convertToYUV420P: Output frame format:" << av_get_pix_fmt_name((AVPixelFormat)m_frame->format)
    //         << "(" << m_frame->format << ")"
    //         << "size:" << m_frame->width << "x" << m_frame->height
    //         << "linesizes:" << m_frame->linesize[0] << m_frame->linesize[1] << m_frame->linesize[2]; // << m_frame->linesize[3];
    //// --- 调试输出结束 ---


    int ret = sws_scale(m_swsContext,            // 缩放上下文
                        decodedFrame->data,      // 输入图像数据 (解码后的)
                        decodedFrame->linesize,  // 输入图像行大小
                        0,                       // 开始行
                        decodedFrame->height,    // <<< 尝试使用解码帧的实际高度
                        m_frame->data,           // 输出图像数据 (目标 YUV420P)
                        m_frame->linesize);      // 输出图像行大小

    if (ret < 0)
    {
        showError(ret); // 错误信息会包含 -22
        return false;
    }
    // 如果成功，ret 应该是输出的高度 (m_frame->height)
    if (ret != m_frame->height) {
        qWarning() << "sws_scale returned unexpected height:" << ret << "expected:" << m_frame->height;
        // 可能不是致命错误，但值得注意
    }


    // 将时间戳等信息从解码帧复制到输出帧（如果需要）
    // av_frame_copy_props(m_frame, decodedFrame); // 更推荐的方式
    m_frame->pts = decodedFrame->pts;


    return true;
}

void VideoDecode::close()
{
    clear(); // 可能不需要，因为 free 会处理
    free();

    m_videoIndex = -1; // 重置索引
    m_totalFrames = 0;
    m_frameRate = 0;
}

void VideoDecode::showError(int err)
{
#if PRINT_LOG
    memset(m_error, 0, ERROR_LEN);
    av_strerror(err, m_error, ERROR_LEN);
    qWarning() << "VideoDecode Error (" << err << "):" << m_error;
#else
    Q_UNUSED(err)
#endif
}

qreal VideoDecode::rationalToDouble(AVRational* rational)
{
    if (!rational || rational->den == 0) return 0.0;
    return (qreal(rational->num) / rational->den);
}

void VideoDecode::clear()
{
    // 清理解码器缓冲区
    if (m_codecContext) {
        avcodec_flush_buffers(m_codecContext);
    }
}

// 在 free() 中添加对 m_decodedFrame 的释放
void VideoDecode::free()
{
    if (m_swsContext) {
        sws_freeContext(m_swsContext);
        m_swsContext = nullptr;
    }
    if (m_codecContext) {
        avcodec_close(m_codecContext); // 先关闭解码器
        avcodec_free_context(&m_codecContext);
    }
    if (m_formatContext) {
        avformat_close_input(&m_formatContext);
    }
    if (m_packet) {
        av_packet_free(&m_packet);
    }
    if (m_frame) {
        // av_freep(&m_frame->data[0]); // av_frame_free 会处理 buffer
        av_frame_free(&m_frame);
    }
    // 释放解码后的帧 ---
    if (m_decodedFrame) {
        av_frame_free(&m_decodedFrame);
    }
}
